﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace SYH.Common.Utilities
{
    /// <summary>
    /// 一组针对类属性处理的使用 Lambda 的扩展方法，一个基本的用法例子：
    /// var fieldName=EntityPropertyHelper.For<Customer>(x=>x.Address.Region)，返回类的属性名称;
    /// </summary>
    public static class EntityPropertyHelper
    {
        public static string For<T, TValue>(Expression<Func<T, TValue>> expression)
        {
            var body = expression.Body;
            return GetMemberName(body);
        }

        public static string For<T>(Expression<Func<T, object>> expression)
        {
            var body = expression.Body;
            return GetMemberName(body);
        }

        public static string For(Expression<Func<object>> expression)
        {
            var body = expression.Body;
            return GetMemberName(body);
        }

        public static string GetMemberName(Expression expression)
        {
            if (expression is MemberExpression)
            {
                var memberExpression = (MemberExpression)expression;
                if (memberExpression.Expression.NodeType ==
                   ExpressionType.MemberAccess)
                    return GetMemberName(memberExpression.Expression)
                      + "." + memberExpression.Member.Name;
                return memberExpression.Member.Name;
            }
            if (expression is UnaryExpression)
            {
                var unaryExpression = (UnaryExpression)expression;
                if (unaryExpression.NodeType != ExpressionType.Convert)
                    throw new Exception(string.Format("无法从表达式 {0} 解析出对象的成员。", expression));
                return GetMemberName(unaryExpression.Operand);
            }
            throw new Exception
              (string.Format("无从表达式 {0} 中提取对象的成员。", expression));
        }

        public static T Set<T, TProperty>(this T obj, Expression<Func<T, TProperty>> field, TProperty value)
        {
            var fn = ((MemberExpression)field.Body).Member.Name;
            obj.GetType().GetProperty(fn).SetValue(obj, value, null);
            return obj;
        }

        public static object GetMemberValue<T, T1>(this T obj, Expression<Func<T, T1>> property)
        {
            var pn = ((MemberExpression)property.Body).Member.Name;
            return obj.GetType().GetProperty(pn).GetValue(obj, null);
        }

        public static Expression AndAlso(this Expression left, Expression right) => Expression.AndAlso(left, right);

        public static Expression OrElse(this Expression left, Expression right) => Expression.OrElse(left, right);

        public static Expression Call(this Expression instance, string methodName, params Expression[] arguments) => Expression.Call(instance, instance.Type.GetMethod(methodName), arguments);

        public static Expression Property(this Expression expression, string propertyName) => Expression.Property(expression, propertyName);

        public static Expression GreaterThan(this Expression left, Expression right) => Expression.GreaterThan(left, right);

        public static Expression Equal(this Expression left, Expression right) => Expression.Equal(left, right);

        public static Expression ToLambda(this Expression body, params ParameterExpression[] parameters) => Expression.Lambda(body, parameters);

        public static Expression<TDelegate> ToLambda<TDelegate>(this Expression body, params ParameterExpression[] parameters) => Expression.Lambda<TDelegate>(body, parameters);
    }
}
